package indv.Cshen.cfengsecurityshirodemo.realm;

import indv.Cshen.cfengsecurityshirodemo.domain.ShiroUser;
import indv.Cshen.cfengsecurityshirodemo.service.ShiroUserService;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.springframework.context.annotation.Lazy;

import javax.annotation.Resource;

/**
 * @author Cfeng
 * @date 2022/7/19
 * Shiro的关键对象，代表的是系统资源，就是一个安全资源的Dao，认证用户之后访问安全的资源
 * 该类就是设置本项目的相关的资源 ，继承AuthorizingRealm,授权范围； 还有一个认证的，一定注意是继承授权系统资源
 */

@Slf4j
@NoArgsConstructor
public class UserNameRealm extends AuthorizingRealm {

    /**
     * 认证就是将当前登录的Subject与数据库中用户比对，成功就将当前的User与特定的身份进行绑定
     * 授权就是对于有身份的用户，在知晓身份时，将特定的角色与权限与其身份绑定
     */
    @Lazy
    @Resource
    private ShiroUserService shiroUserService;

    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        log.info("entered MyReamlm doGetAuthorizationInfo method");
        //获得用户，从认证的info中直接获取的principal
        ShiroUser user = (ShiroUser) principalCollection.asList().get(0); //第一个就是下面的user
        //创建一个授权Info
        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
        //需要绑定的角色资源
        simpleAuthorizationInfo.addRoles(user.getUserRoles());
        simpleAuthorizationInfo.addStringPermissions(user.getUserPermissions());
        return simpleAuthorizationInfo;
    }

    //认证，返回的是身份和密码principal和credentials
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        log.info("======entered MyReamlm doGetAuthenticationInfo method");
        //之前的login登录验证最后要访问Realm，当时的token最后会传入给Realm，所以这里的token就是之前的token
        //所以类型其实是相同的，可以强制类型转换
        UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) authenticationToken;
        //获取数据库中的用户，与当前用户进行比较，这里是使用的Service进行查询
        String userName = usernamePasswordToken.getUsername();
        ShiroUser user = shiroUserService.queryUserByName(userName);
        if(null == user) {
            return null; //这里就会在login位置抛出账户不能存在的异常
        }
        //指定数据库中salt加密方式的salt，数据库密码加密使用SimpleHash即可,demo中每一个用户加的salt为用户名
        ByteSource salt = ByteSource.Util.bytes(user.getUserName());
        //找到之后将查询到的对象返回
        //返回AuthentiacationInfo，完成认证，密码比对由Shiro完成，因为很敏感，构造器中还可以加入salt
        SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(user,user.getUserPwd(),salt,this.getName());
        return simpleAuthenticationInfo;
    }
}
